﻿using System;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Collections.ObjectModel;
using System.Linq;
using OAuthLib;
using System.ComponentModel;
using WTwitter.Model.Framework;

namespace WTwitter.Model.UserData {
	/// <summary>
	/// ユーザー設定を保存するクラス
	/// publicフィールド（リストの参照先含む）はXmlSerializerで保存される
	/// </summary>
	public class Option {
		//TODO シングルトンの構造にしているが、UserInfoなどはUserInfoのインスタンスをコンストラクタを通じてTimelineなどに渡している
		//アクセスの方法はInstanceプロパティを通して、に統一するかどうか検討。
		//将来のマルチアカウント対応の可能性を考慮にいれると、Timelineには個別にUserInfoを渡しておいた方がいいかも。

		private static Option _instance;
		public static Option Instance {
			get {
				if (_instance == null) {
					_instance = Load("Setting.xml");
				}
				return _instance;
			}
		}

		private Option() {
		}

		#region Property
		public AuthInfo UserInfo { get; set; }

		public BitlySetting Bitly { get; set; }

		public List<FilterSetting> Filters { get; set; }

		public string Skin { get; set; }

		[XmlIgnore]
		public AccessToken AccessTokenTmp { get; set; }

		[XmlArrayItem(typeof(TimelineSetting))]
		[XmlArrayItem(typeof(SearchTimelineSetting))]
		[XmlArrayItem(typeof(DirectMessageSetting))]
		public ObservableCollection<TimelineSetting> TimelineSettings { get; set; }
		#endregion

		#region Public Method
		/// <summary>
		/// ファイルがないときなどにデフォルト値を読み込む
		/// </summary>
		/// <returns></returns>
		static private Option LoadDefault() {
			var instance = new Option();
			instance.UserInfo = new AuthInfo();
			instance.Bitly = new BitlySetting() {
				LoginName = "ojisan",
				Version = "2.0.1",
				APIKey = "R_6edce4b26787a70bd11a7a1c570f52f8",
			};
			instance.Filters = new List<FilterSetting>() {
				new FilterSetting() {Target = FilterTargetType.Id, ngList = new List<string>()}
				};
			instance.TimelineSettings = new ObservableCollection<TimelineSetting>();
			var list = new TimelineSetting[] {
				TimelineSettingTemplate.Home.Generator(),
				TimelineSettingTemplate.Mentions.Generator()
			};
			foreach (var item in list) {
				instance.TimelineSettings.Add(item);
			}

			instance.Skin = "Default";

			return instance;
		}

		/// <summary>
		/// nullのプロパティに対して空のインスタンス作成する
		/// ※VerUpでプロパティ追加したときに、Load()するとSetting.xmlに存在しないアイテムはnullになるため
		/// </summary>
		private void LoadDefaultForNullProperty() {
			if (UserInfo == null) UserInfo = new AuthInfo();
			if (Bitly == null) Bitly = new BitlySetting() {
				LoginName = "ojisan",
				Version = "2.0.1",
				APIKey = "R_6edce4b26787a70bd11a7a1c570f52f8",
			}; 
			if (Filters == null) {
				Filters = new List<FilterSetting>() {
					new FilterSetting() {Target = FilterTargetType.Id, ngList = new List<string>()}
				};
			}
			if (TimelineSettings == null) TimelineSettings = new ObservableCollection<TimelineSetting>();
			if (string.IsNullOrEmpty(Skin)) Skin = "Default";
		}

		/// <summary>
		/// Optionの内容をファイルに保存する
		/// </summary>
		/// <param name="filename">保存ファイル名</param>
		public void Save(string filename) {
			UserInfo.AccessToken = this.AccessTokenTmp;
			var serializer = new XmlSerializer(this.GetType());
			using (var stream = new FileStream(filename, FileMode.Create)) {
				serializer.Serialize(stream, this);
			}
		}

		/// <summary>
		/// Optionの内容をファイルに保存する。ファイル名はLoad()時に使用したもの。
		/// </summary>
		public void Save() {
			Debug.Assert(!string.IsNullOrEmpty(this._filename));
			this.Save(this._filename);
		}

		/// <summary>
		/// ファイルからOptionクラスを読み込む
		/// </summary>
		/// <param name="filename">読み込むファイル名</param>
		/// <returns>読み込んだオブジェクト</returns>
		static public Option Load(string filename) {
			//ファイルがなかったらデフォルト値を読み込む
			if (!File.Exists(filename)) {
				var newInstance = LoadDefault();
				newInstance._filename = filename;
				return newInstance;
			}

			//読み込み
			var serializer = new XmlSerializer(typeof(Option));
			Option instance;
			using (var stream = new FileStream(filename, FileMode.Open)) {
				instance = serializer.Deserialize(stream) as Option;
			}
			instance.AccessTokenTmp = instance.UserInfo.AccessToken;
			//Save()の時のためにファイル名を保存
			instance._filename = filename;
			instance.LoadDefaultForNullProperty();
			return instance;
		}

		public void AddTimeline(TimelineSettingTemplate template) {
			this.TimelineSettings.Add(template.Generator());
		}

		public void AddNg(FilterTargetType targeType, string ngText) {
			var filter = Filters.SingleOrDefault(item => item.Target == targeType);
			if (filter == null) {
				Filters.Add(
					new FilterSetting() { Target = targeType, ngList = new List<string>() { ngText } }
				);
			} else {
				filter.ngList.Add(ngText);
			}
			this.Save();
		}

		public void RemoveTimeline(TimelineSetting target) {
			Debug.Assert(target != null && target is TimelineSetting && TimelineSettings.Contains(target),
				 "RemoveTimeline: 引数が不正");
			this.TimelineSettings.Remove(target);
		}

		public void UpTimelineOrder(TimelineSetting target) {
			var index = TimelineSettings.IndexOf(target);
			if (index == 0) {
				return;
			}
			TimelineSettings.Move(index, index - 1);
		}

		public void DownTimelineOrder(TimelineSetting target) {
			var index = TimelineSettings.IndexOf(target);
			if (index == TimelineSettings.Count - 1) {
				return;
			}
			TimelineSettings.Move(index, index + 1);
		}
		#endregion Public Method

		#region private member
		private string _filename;
		#endregion
	}
}
